home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 May: Tool Chest / Dev.CD May 98 TC.toast / Tool Chest / Development Kits / HyperCard Related / APDA HyperCard Toolkits / HyperCard CTB Toolkit 1.0b2 / Source Code / CTBUtil.inc < prev    next >
Encoding:
Text File  |  1995-02-07  |  11.2 KB  |  413 lines  |  [TEXT/MPS ]

  1. {        Miscellaneous routines used with the Communication Toolbox XCMDs.
  2.  
  3.     This file is included in the other Comm Toolbox XCMD source code files -- i.e., it is not compiled and
  4.     linked separately.
  5.  
  6.     Copyright © 1990 Apple Computer, Inc.
  7.  
  8.     Initial coding 2/90 by Harry Chesley.
  9. }
  10.  
  11. procedure Fail(errMsg: Str255); forward;
  12.  
  13. const
  14.  
  15. GLOBALNAME = 'xyzzyCTBGlobals';            { Where to store a handle to our globals. }
  16.  
  17. BUFFERSIZE = 1024;                                    { Connection input buffer used within the XCMD. }
  18.  
  19. type
  20.  
  21. { Tool type: }
  22. ToolType = (connectionTool, terminalTool, fileTransferTool);
  23.  
  24. { Handle storage for one tool. }
  25. OneToolType =
  26.     record
  27.         case tType: ToolType of
  28.             connectionTool: (cHand: ConnHandle);            { A connection tool. }
  29.             terminalTool: (tHand: TermHandle);                { A terminal tool. }
  30.             fileTransferTool: (ftHand: FTHandle)                { A file transfer tool. }
  31.     end;
  32.  
  33. ToolArray = array [1..1000] of OneToolType;
  34.  
  35. { An input buffer. }
  36. InputBufferType =
  37.     record
  38.         bufferPtr: Ptr;                                                    { Pointer to next byte to get from buffer. }
  39.         amountLeft: longInt;                                            { Number of bytes left in the buffer. }
  40.         timeOut: longInt;                                                { How long to try before timing out. }
  41.         termString: Handle;                                            { String to stop receiving after. }
  42.         termOffset: longInt;                                            { How much of the termString we've seen. }
  43.         recvLimit: longInt;                                            { Max. number of chars to receive at once. }
  44.         doStrip: boolean;                                                { Strip controls & top bit. }
  45.         buffer: array [1..BUFFERSIZE] of SignedByte;    { The input buffer. }
  46.     end;
  47.  
  48. InputBufferPtr = ^InputBufferType;
  49. InputBufferHandle = ^InputBufferPtr;
  50.  
  51. { Global data: }
  52. OurGlobalType =
  53.     record
  54.         connHand: ConnHandle;            { Connection tool handle. }
  55.         termHand: TermHandle;        { Terminal tool handle. }
  56.         FTHand: FTHandle;                { File transfer tool handle. }
  57.         allToolsSize: integer;            { Number of outstanding tools. }
  58.         allTools: ToolArray;                { Array of outstanding tools (extended dynamically). }
  59.     end;
  60.  
  61. OurGlobalPtr = ^OurGlobalType;
  62. OurGlobalHandle = ^OurGlobalPtr;
  63.  
  64. var
  65.  
  66. { Global data. (Note: Although this is called global, it's actually allocated as local to the top-level
  67.     XCMD routine, and therefore only stays around for the duration of the XCMD execution. Being
  68.     able to think of it as globals, and not having to pass it to each subroutine called, however, is
  69.     extremely useful. This is perhaps the best reason for using Pascal rather than C for writing
  70.     XCMDs.) }
  71.  
  72. Globals: OurGlobalHandle;        { Global data (this handle is saved in a HyperTalk global between XCMDs). }
  73.  
  74. function TrapAvailable(tNumber: integer; tType: TrapType): boolean;
  75.     { Return true if the indicated trap is installed. }
  76.  
  77.     const _UnimplementedToolTrap = $A89F;
  78.         _UnimplementedOSTrap = $9F;
  79.  
  80.     var unImplemented: integer;
  81.  
  82.     begin
  83.         if tType = OSTrap then unImplemented := _UnimplementedOSTrap
  84.         else unImplemented := _UnimplementedToolTrap;
  85.         TrapAvailable := NGetTrapAddress(tNumber, tType) <> GetTrapAddress(unImplemented);
  86.     end;
  87.  
  88. procedure FailOSErr(err: OSErr);
  89.     { Check for an error return and fail if there is one. }
  90.  
  91.     var s: Str255;
  92.  
  93.     begin
  94.         if err <> noErr then
  95.             begin
  96.                 LongToStr(paramPtr,err,s);
  97.                 Fail(Concat('Failed due to OS error ',s));
  98.             end;
  99.     end;
  100.  
  101. procedure GetStrGlobal(name: str255; var glob: str255);
  102.     { Set glob to the global string specified by name. }
  103.  
  104.     var globHand: Handle;
  105.  
  106.     begin
  107.         { Get the HyperTalk global. }
  108.         globHand := GetGlobal(paramPtr,name);
  109.         { Convert it to a Pascal string. }
  110.         if globHand = nil then glob := ''
  111.         else
  112.             begin
  113.                 ZeroToPas(paramPtr,globHand^,glob);
  114.                 DisposHandle(globHand);
  115.             end;
  116.     end;
  117.  
  118. function GetLongGlobal(name: str255): longInt;
  119.     { Return the global string specified by name, interpreted as a long integer. }
  120.  
  121.     var globStr: str255;
  122.  
  123.     begin
  124.         { Get the HyperTalk global into a Pascal string. }
  125.         GetStrGlobal(name,globStr);
  126.         { Convert it to a longInt. }
  127.         GetLongGlobal := StrToLong(paramPtr,globStr);
  128.     end;
  129.  
  130. procedure SetStrGlobal(name: str255; glob: str255);
  131.     { Set the global string specified by name to glob. }
  132.  
  133.     var globHand: Handle;
  134.  
  135.     begin
  136.         { Convert the string to a HyperTalk style handle-string. }
  137.         globHand := PasToZero(paramPtr,glob);
  138.         { Set the global. }
  139.         SetGlobal(paramPtr,name,globHand);
  140.         { Dispose of our copy. }
  141.         DisposHandle(globHand);
  142.     end;
  143.  
  144. procedure SetLongGlobal(name: str255; globLong: longInt);
  145.     {Set the global string specified by name to a string that represents the number in globLong. }
  146.  
  147.     var globStr: str255;
  148.  
  149.     begin
  150.         { Convert the longInt to a Pascal string. }
  151.         LongToStr(paramPtr,globLong,globStr);
  152.         { Set the HyperTalk global to that. }
  153.         SetStrGlobal(name,globStr);
  154.     end;
  155.  
  156. procedure GetStrParm(n: integer; var str: str255);
  157.     { Get the nth parameter into str. }
  158.  
  159.     begin
  160.         if paramPtr^.params[n] = nil then str := ''
  161.         else ZeroToPas(paramPtr,paramPtr^.params[n]^,str);
  162.     end;
  163.  
  164. function GetLongParm(n: integer): longInt;
  165.     { Return the nth parameter string, interpreted as a long integer. }
  166.  
  167.     var str: str255;
  168.  
  169.     begin
  170.         ZeroToPas(paramPtr,paramPtr^.params[n]^,str);
  171.         GetLongParm := StrToNum(paramPtr,str);
  172.     end;
  173.  
  174. function GetToolTypeParm(n: integer): ToolType;
  175.     { Return the type of the tool specified by parameter n ("connection", "terminal", or "file transfer"). }
  176.  
  177.     var ch: Char;
  178.  
  179.     begin
  180.         ch := Chr(paramPtr^.params[n]^^);
  181.         if (ch = 't') or (ch = 'T') then GetToolTypeParm := terminalTool
  182.         else if (ch = 'f') or (ch = 'F') then GetToolTypeParm := fileTransferTool
  183.         else GetToolTypeParm := connectionTool;
  184.     end;
  185.  
  186. function ParmPresent(n: integer): boolean;
  187.     { Return true if the parameter is present and non-empty. }
  188.  
  189.     var s: Str255;
  190.  
  191.     begin
  192.         if n > paramPtr^.paramCount then ParmPresent := false
  193.         else
  194.             begin
  195.                 GetStrParm(n,s);
  196.                 if s = '' then ParmPresent := false
  197.                 else ParmPresent := true;
  198.             end;
  199.     end;
  200.  
  201. function min(l1, l2: longInt): longInt;
  202.  
  203.     begin
  204.         if l1 < l2 then min := l1
  205.         else min := l2;
  206.     end;
  207.  
  208. function max(l1, l2: longInt): longInt;
  209.  
  210.     begin
  211.         if l1 > l2 then max := l1
  212.         else max := l2;
  213.     end;
  214.  
  215. function CTBInstalled: boolean;
  216.     { Return true if the Comm Toolbox is installed. }
  217.  
  218.     const _CommToolboxTrap = $8B;
  219.  
  220.     begin
  221.         CTBInstalled := TrapAvailable(_CommToolboxTrap, OSTrap);
  222.     end;
  223.  
  224. procedure CTBReady;
  225.     { If we haven't been here before, create the handle we use for our global variables,
  226.         remember it in a HyperCard global, and call the Comm Toolbox init routines. Also,
  227.         give all the outstanding tools some idle time (since all the XCMDs call CTBReady,
  228.         this insures that everyone gets idle from time to time, so long as the user
  229.         calls some XCMD). }
  230.  
  231.     var ignore: integer;
  232.         i: integer;
  233.  
  234.     procedure idleOne(theTool: OneToolType);
  235.         { Give the tool idle time. }
  236.  
  237.         var sizes: CMBufferSizes;
  238.             status: CMStatFlags;
  239.  
  240.         begin
  241.             case theTool.tType of
  242.                 connectionTool:
  243.                     begin
  244.                         { Idle the connection. }
  245.                         CMIdle(theTool.cHand);
  246.                         { If there's an incoming connection request, answer it. }
  247.                         FailOSErr(CMStatus(theTool.cHand,sizes,status));
  248.                         if BAnd(status,cmStatusIncomingCallPresent) <> 0 then
  249.                             FailOSErr(CMAccept(theTool.cHand,true));
  250.                     end;
  251.                 terminalTool:
  252.                     { Idle the terminal tool. }
  253.                     TMIdle(theTool.tHand);
  254.                 end;
  255.         end;
  256.  
  257.     begin
  258.         { Get the value of the global that holds our globals handle. }
  259.         Globals := OurGlobalHandle(GetLongGlobal(GLOBALNAME));
  260.         { If it's empty (which will evaluate to zero or nil), then we need to create it. }
  261.         if Globals = nil then
  262.             begin
  263.                 { Make sure the Toolbox is here. }
  264.                 if not CTBInstalled then Fail('Comm Toolbox not installed');
  265.                 { Make the handle. }
  266.                 Globals := OurGlobalHandle(NewHandle(sizeof(OurGlobalType)-sizeof(ToolArray)));
  267.                 if Globals = nil then Fail('Could not allocate global variable space');
  268.                 { Remember it in a HyperCard global. }
  269.                 SetLongGlobal(GLOBALNAME,ord4(Globals));
  270.                 { Default to nothing happenin'. }
  271.                 with Globals^^ do
  272.                     begin
  273.                         connHand := nil;
  274.                         termHand := nil;
  275.                         FTHand := nil;
  276.                         allToolsSize := 0;
  277.                     end;
  278.                 { Initialize the Comm Toolbox. }
  279.                 ignore := InitCTBUtilities;
  280.                 ignore := InitCRM;
  281.                 ignore := InitCM;
  282.                 ignore := InitTM;
  283.                 ignore := InitFT;
  284.             end;
  285.         { Now idle everyone. }
  286.         for i := 1 to Globals^^.allToolsSize do idleOne(Globals^^.allTools[i]);
  287.     end;
  288.  
  289. procedure DeallocateGlobals;
  290.  
  291.     begin
  292.         DisposHandle(Handle(Globals));
  293.         SetLongGlobal(GLOBALNAME,0)
  294.     end;
  295.  
  296. procedure EnsurePresent(tt: ToolType);
  297.     { Check if there is a handle of the right tool type. If not, fail. }
  298.  
  299.     var noGood: boolean;
  300.  
  301.     begin
  302.         noGood := false;
  303.         if ((tt = connectionTool) and (Globals^^.connHand = nil)) or
  304.             ((tt = terminalTool) and (Globals^^.termHand = nil)) or
  305.             ((tt = fileTransferTool) and (Globals^^.FTHand = nil)) then Fail('Error: must do New first');
  306.     end;
  307.  
  308. procedure EnsureOpen;
  309.     { Check if the connection is open. If it isn't, then open it now. }
  310.  
  311.     var sizes: CMBufferSizes;
  312.         status: CMStatFlags;
  313.  
  314.     begin
  315.         { Is it already open? }
  316.         FailOSErr(CMStatus(Globals^^.connHand,sizes,status));
  317.         { If not, open it. }
  318.         if BAnd(status,cmStatusOpen+cmStatusOpening) = 0 then
  319.             FailOSErr(CMOpen(Globals^^.connHand,false,nil,-1));
  320.     end;
  321.  
  322. function ReadFromConn(buf: Ptr; sz: longInt): longInt;
  323.     { Read sz bytes from the current connection into the buffer pointed to by buf. }
  324.  
  325.     var toRead: longInt;
  326.         l, l2: longInt;
  327.         p: Ptr;
  328.         flags: CMFlags;
  329.  
  330.     begin
  331.         { Read the bytes into the buffer. }
  332.         toRead := sz;
  333.         if CMRead(Globals^^.connHand,buf,toRead,cmData,false,nil,-1,flags) <> noErr then
  334.             Fail('Read failed');
  335.         { If there's a terminal emulator present, feed the bytes to it as well. }
  336.         if Globals^^.termHand <> nil then
  337.             begin
  338.                 { Loop in case it doesn't eat them all in one gulp. }
  339.                 p := buf;
  340.                 l := toRead;
  341.                 while l > 0 do
  342.                     begin
  343.                         l2 := TMStream(Globals^^.termHand,p,l,flags);
  344.                         p := Ptr(ord4(p)+l2);
  345.                         l := l - l2;
  346.                     end;
  347.             end;
  348.         { Return the number we actually succeeded in reading. }
  349.         ReadFromConn := toRead;
  350.     end;
  351.  
  352. procedure StripBytes(h: Handle; sz: longInt; doStrip: boolean);
  353.     { Mung a handle of data around to the right format to return to HyperCard. Truncate it
  354.         so there's only sz bytes plus a zero termination. Add the zero termination. If doStrip
  355.         is true, strip off control characters other than tab and return, and clear the top bit
  356.         of each byte. If doStrip is false, replace nulls (which would terminate a HyperCard
  357.         string) with "¿"s. }
  358.  
  359.     var p, p2: Ptr;
  360.         b: SignedByte;
  361.         l: longInt;
  362.         newSz: longInt;
  363.  
  364.     begin
  365.         { To strip or not to strip... }
  366.         if doStrip then
  367.             begin
  368.                 { Cycle through all the bytes. }
  369.                 p := h^;
  370.                 p2 := p;
  371.                 l := sz;
  372.                 newSz := 0;
  373.                 while l > 0 do
  374.                     begin
  375.                         { Strip the top bit. }
  376.                         b := p^;
  377.                         b := BAnd(b,$7F);
  378.                         { If it's not a control character or is a tab or a return, keep it. }
  379.                         if (b >= ord(' ')) or (b = 9) or (b = 13) then
  380.                             begin
  381.                                 p2^ := b;
  382.                                 p2 := Ptr(ord4(p2)+1);
  383.                                 newSz := newSz+1;
  384.                             end;
  385.                         p := Ptr(ord4(p)+1);
  386.                         l := l-1;
  387.                     end;
  388.                 { Set the handle size appropriately. }
  389.                 SetHandleSize(h,newSz+1);
  390.                 { Add in the termination null. }
  391.                 p2 := Ptr(ord4(h^) + newSz);
  392.                 p2^ := 0;
  393.             end
  394.         else
  395.             begin
  396.                 { Cycle through looking for nulls. }
  397.                 p := h^;
  398.                 l := sz;
  399.                 while l > 0 do
  400.                     begin
  401.                         { If we find a null, eradicate it. }
  402.                         if p^ = 0 then p^ := ord('¿');
  403.                         p := Ptr(ord4(p)+1);
  404.                         l := l-1;
  405.                     end;
  406.                 { Set the handle size appropriately. }
  407.                 SetHandleSize(h,sz+1);
  408.                 { Add in the termination null. }
  409.                 p := Ptr(ord4(h^) + sz);
  410.                 p^ := 0;
  411.             end;
  412.     end;
  413.